/*
 * Server.cpp
 *
 *  Created on: 2009-01-10
 *      Author: tomaszd
 */

#include "UdpServer.h"
#include <arpa/inet.h>
#include <errno.h>
#include <fstream>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdio.h>

using namespace std;

#define CZAS_OCZEKIWANIA_NA_GRACZY 120
#define CZAS_OCZEKIWANIA_PAKIET_S 5

UdpServer::UdpServer() {
	addr_len = sizeof(struct sockaddr);
	slen = sizeof(struct sockaddr);
}

UdpServer::~UdpServer() {
	// TODO Auto-generated constructor stub
}

/*=========================================================================================*/

void UdpServer::udpServerConfig() {
    string line;
    int i = 1;
    ifstream config_file;
	config_file.open("udp-server/options.cfg", ios::in);
	if (config_file.is_open()) {
		cout << "Odczytuje plik konfiguracyjny..." << endl;

		while(!config_file.eof()) {
			line="";
			getline(config_file, line);
			if (!line.compare(";"))
				break;
			int col = line.find('=', 0);
			line = line.substr(col+1, line.length());
			switch(i) {
				case 1:
					udp_server_options.server_port = atoi(line.c_str());
					break;
				case 2:
					inet_aton(line.c_str(), &(udp_server_options.game_addr));
					break;
				case 3:
					udp_server_options.game_port = atoi(line.c_str());
					break;
				case 4:
					udp_server_options.passwd = atoi(line.c_str());
					break;
				default:
					cout << "Blad w pliku konfiguracyjnym!" << endl;
					exit(EXIT_FAILURE);
			}

			i++;
		}

		config_file.close();

//		cout << "Port serwera	     : " << UdpServer_options.UdpServer_port << endl;
//		cout << "Adres multicastowy   : " << inet_ntoa(UdpServer_options.game_addr) << endl;
//		cout << "Port multicastowy    : " << UdpServer_options.game_port << endl;
	}
	else {
		cout<<"Upsss...\tNie znalazłem pliku konfiguracyjnego!"<<endl;
		exit(EXIT_FAILURE);
	}
}

/*=========================================================================================*/

void UdpServer::init() {
    const unsigned char ttl = 3;		// wartosc 'timetolive'
	const unsigned char loop = 1;

	// wypelniamy strukture przechowujaca adres lokalny serwera
	udp_server_addr.sin_family = AF_INET;
	udp_server_addr.sin_port = htons(udp_server_options.server_port);
	udp_server_addr.sin_addr.s_addr = INADDR_ANY;
	memset(&(udp_server_addr.sin_zero), '\0', 8);

	// wypelniamy strukrure przechowywujaca adres gry
	game_addr.sin_family      = AF_INET;
	game_addr.sin_port        = htons(udp_server_options.game_port);
	game_addr.sin_addr = udp_server_options.game_addr;
    memset(&(game_addr.sin_zero), '\0', 8);

	if ((sh = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))== -1) {
		perror("socket");
		exit(EXIT_FAILURE);
	}

	if (bind(sh, (struct sockaddr*)&udp_server_addr, addr_len) == -1) {
		perror("bind");
		exit(EXIT_FAILURE);
	}


	// ustawiamy opcje 'IP multicast timetolive' socket-a
    if (setsockopt(sh, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(unsigned char)) == -1) {
    	perror("setsockopt");
		exit(EXIT_FAILURE);
    }

	// ustawiamy opcje 'IP multicast loopback' socket-a
    if (setsockopt(sh, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(unsigned char)) == -1) {
    	perror("setsockopt multicast loop");
		exit(EXIT_FAILURE);
    }

    // ustawiamy wychodządzy interfejs dla multicast-u na domyslny
    in_addr def;
    def.s_addr = INADDR_ANY;
    if (setsockopt(sh, IPPROTO_IP, IP_MULTICAST_IF, &def, sizeof(struct in_addr)) == -1) {
    	perror("setsockopt multicast if");
		exit(EXIT_FAILURE);
    }
}

/*=========================================================================================*/

void UdpServer::printUdpPlayers() {
	cout << "Gracze bioracy udzial w grze" << endl;
	if(udpPlayers_list.size() == 0)
        cout << "Brak graczy." << endl;
	else {
		for(unsigned int i=0; i<udpPlayers_list.size(); i++) {
			cout << "\t" << udpPlayers_list[i].nick << " - host : " << inet_ntoa(udpPlayers_list[i].udp_player_addr.sin_addr) << endl;
		}
	}
}

/*=========================================================================================*/

bool UdpServer::chceckNickUnique (char *name) {
	for(unsigned int i=0; i<udpPlayers_list.size(); i++) {
		if (strcmp(udpPlayers_list[i].nick, name) == 0) {
			cout << "Nieunikalny nick gracza!" << endl;
			return false;
		}
	}
	return true;
}

/*=========================================================================================*/

int UdpServer::addUdpPlayers() {
	// ustawiamy limit czasu oczekiwania funkcji 'recvfrom'
	tv.tv_sec = CZAS_OCZEKIWANIA_NA_GRACZY;
	tv.tv_usec = 0;
#ifdef TIMEOUT_SUPPPORTED		
    if (setsockopt(sh, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) == -1) {
    	perror("setsockopt_recv");
		return EXIT_FAILURE;
    }
#endif 
    cout << "Czekamy na zgloszenia graczy..." << endl;

	while (true) {
		if (recvfrom(sh, (char*)&udp_player_decl, sizeof(declaration),0 , (struct sockaddr*)&udp_player_addr, &slen) == -1) {
			if (errno == 11) {
				cout << "\n\tUplynal czas oczekiwania na zgloszenia.\n" << endl;
				return 0;
			}
			else {
				perror("recvfrom");
				exit(EXIT_FAILURE);
			}
		}

		if (udp_player_decl.passwd == udp_server_options.passwd && chceckNickUnique(udp_player_decl.nick)) {
			init_struct.accept = 1;
			init_struct.addr = game_addr;
			if (sendto(sh, (char*)&init_struct, sizeof(struct init), 0, (struct sockaddr*)&udp_player_addr, addr_len) == -1) {
				perror("sendto");
				exit(EXIT_FAILURE);
			}
			UdpPlayer new_udpPlayer(udp_player_addr, udp_player_decl.nick);	// tworzymy nowego gracza
			udpPlayers_list.push_back(new_udpPlayer);					// dodajemy go do listy graczy
			udp_player_decl.passwd = 0;							// zerujemy jego flage
			cout << "Gracz " << new_udpPlayer.nick << " dodany do listy graczy.\n";
			return 0;
		}
		else if (udp_player_decl.passwd == udp_server_options.passwd) {
			init_struct.accept = -1;
			init_struct.addr = none_addr;
			if (sendto(sh, (char*)&init_struct, sizeof(struct init), 0, (struct sockaddr*)&udp_player_addr, addr_len) == -1) {
				perror("sendto");
				exit(EXIT_FAILURE);
			}
		}
		else {
			cout << "Nieudana proba uwierzytelnienia!" << endl;
			init_struct.accept = 0;
			init_struct.addr = none_addr;
			if (sendto(sh, (char*)&init_struct, sizeof(struct init), 0, (struct sockaddr*)&udp_player_addr, addr_len) == -1) {
				perror("sendto");
				exit(EXIT_FAILURE);
			}
		}

	}
	printUdpPlayers();
	return 0;
}

/*=========================================================================================*/

void UdpServer::send(char *str, unsigned long int size) {
//	cout << "send()" << endl;
	if (sendto(sh, str, size, 0, (struct sockaddr*)&game_addr, addr_len) == -1) {
		perror("sendto");
		exit(EXIT_FAILURE);
	}
}

/*=========================================================================================*/

int UdpServer::receiveUdpPlayersMoves (unsigned long int sn) {
	// ustawiamy limit czasu oczekiwania funkcji 'recvfrom'
	tv.tv_sec = CZAS_OCZEKIWANIA_PAKIET_S;
	tv.tv_usec = 0;
#ifdef TIMEOUT_SUPPPORTED		
    if (setsockopt(sh, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) == -1) {
    	perror("setsockopt");
		exit(EXIT_FAILURE);
    }
#endif 
    //cout << "Czekamy na ruch graczy..." << endl;

	while (true) {
		if (recvfrom(sh, (char*)&udp_player_move, sizeof(udp_player_move),0 , (struct sockaddr*)&udp_player_addr, &slen) == -1) {
			if (errno == 11) {
				cout << "\n\tUplynal czas oczekiwania.\n" << endl;
				return -11;
			}
			else {
				perror("recvfrom");
				
			}
		}

		/*if (udp_player_move.round == sn) { */
    		for(unsigned int i=0; i<udpPlayers_list.size(); i++) {
    			if(inet_ntoa(udpPlayers_list[i].udp_player_addr.sin_addr) == inet_ntoa(udp_player_addr.sin_addr)) {
    				udpPlayers_list[i].last_dir = udp_player_move.d;
    		//		cout << "Gracz " << udpPlayers_list[i].nick << " podaza w " << udp_player_move.d << endl;
    		//		udpPlayers_list[i].last_dir= udp_player_move.d;
    			}
    		}
    		break;
		/*}*/
	}
    return 0;
}

/*=========================================================================================*/
